#include "JParser.h"

#include <iostream>
#include <stack>

MY_JSON_PARSER_NAMESPACE_BEGIN
enum JParserState {
	PARSE_CONTINUE = 0,
	PARSE_FINISH,
	PARSE_OBJECT_GET_KEY,
	PARSE_OBJECT_GET_VALUE,
	PARSE_OBJECT_GET_KEY_VALUE_FINISH,
	PARSE_OBJECT_FINISH,
	PARSE_STRING_START,
	PARSE_STRING_GET_NEXT_CHAR,
	PARSE_STRING_FINISH,
	PARSE_ARRAY_GET_VALUE,
	PARSE_NUMBER_START,
	PARSE_INT,
	PARSE_DOUBLE,
	PARSE_NUMBER_FINISH,
	PARSE_SPECIAL_VALUE_START,
	PARSE_SPECIAL_VALUE_FINISH,
	PARSE_NULL,
	PARSE_TRUE,
	PARSE_FALSE,
};

enum JParserErrorType {
	NO_ERROR = 0,
	INCOMPLETE_STRING,
	INDEX_OUT_OF_BOUND,
	KEY_VALUE_CONNECTION,
	KEY_ERROR,
	VALUE_ERROR,
	NO_KEY,
	NO_VALUE,
	KEY_VALUE_FINISH_ERROR,
	PARSE_FINISH_ERROR,
	ARRAY_VALUES_CONNECTION,
	INT_ERROR,
	DOUBLE_ERROR
};
MY_JSON_PARSER_NAMESPACE_END

static std::string jtext = "";

static std::uint64_t jtext_size = 0;

static std::uint64_t cur_index_of_jtext = 0;

static char cur_ch = 0;

static std::stack<char> start_symbol_stack;

static myjson::JParserState cur_parse_state = myjson::JParserState::PARSE_CONTINUE;

static myjson::JNode* parse_result = nullptr;

static void reportError(myjson::JParserErrorType error_type)
{
	std::string preStr = "Index: " + std::to_string(cur_index_of_jtext) + "\n\tError: ";
	switch (error_type)
	{
	case myjson::JParserErrorType::INDEX_OUT_OF_BOUND:
		std::cout << preStr + "Index out of bound!\n";
		break;
	case myjson::JParserErrorType::INCOMPLETE_STRING:
		std::cout << preStr + "The current string is illegal!\n";
		break;
	case myjson::JParserErrorType::KEY_VALUE_CONNECTION:
		std::cout << preStr + "The middle of the key value pair of the object is not a colon!\n";
		break;
	case myjson::JParserErrorType::KEY_ERROR:
		std::cout << preStr + "The key of the object is illegal!\n";
		break;
	case myjson::JParserErrorType::VALUE_ERROR:
		std::cout << preStr + "The value is illegal!\n";
		break;
	case myjson::JParserErrorType::NO_KEY:
		std::cout << preStr + "The key value pair of the object has no key!\n";
		break;
	case myjson::JParserErrorType::NO_VALUE:
		std::cout << preStr + "The key value pair of the object has no value!\n";
		break;
	case myjson::JParserErrorType::KEY_VALUE_FINISH_ERROR:
		std::cout << preStr + "Error at the end of the key value pair!\n";
		break;
	case myjson::JParserErrorType::PARSE_FINISH_ERROR:
		std::cout << preStr + "Error at the end of the JSON text!\n";
		break;
	case myjson::JParserErrorType::ARRAY_VALUES_CONNECTION:
		std::cout << preStr + "The character between the values of the array is not a comma!\n";
		break;
	default:
		break;
	}
}

static std::string removeEmptyCharacters(const std::string& json_text)
{
	std::string ans;
	bool str_start = false;
	for (const char& ch : json_text)
	{
		if (ch == '\"')
		{
			str_start ^= 1;
		}
		if (str_start)
		{
			ans += ch;
		}
		else if (ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r')
		{
			ans += ch;
		}
	}
	return ans;
}

myjson::JSONParser::JSONParser(const std::string& json_text)
{
	jtext = std::string(std::move(removeEmptyCharacters(json_text)));
	jtext_size = jtext.size();
	cur_index_of_jtext = 0;
	if (jtext_size > 0)cur_ch = jtext[cur_index_of_jtext];
	else cur_ch = 0;
	start_symbol_stack = std::stack<char>();
	//cur_parse_state = myjson::JParserState::PARSE_CONTINUE;
	parse_result = nullptr;
}

myjson::JNode& myjson::JSONParser::parse()
{
	if (cur_index_of_jtext < jtext_size)
	{
		parse_result = this->parseValue();
		cur_index_of_jtext++;
	}
	if (cur_index_of_jtext < jtext_size && !start_symbol_stack.empty())
	{
		reportError(myjson::JParserErrorType::PARSE_FINISH_ERROR);
	}
	else if (parse_result == nullptr)
	{
		parse_result = new myjson::JNode();
	}
	return *parse_result;
}

myjson::JNode* myjson::JSONParser::parseObject()
{
	myjson::JNode* ans_jnode = new myjson::JNode(myjson::JNodeType::JSON_OBJECT);
	std::string* cur_key = nullptr;
	myjson::JNode* cur_jnode = nullptr;
	bool if_finish = false;
	while (cur_index_of_jtext < jtext_size && !if_finish)
	{
		cur_ch = jtext[cur_index_of_jtext];
		if (cur_ch == '}')break;
		switch (cur_parse_state)
		{
		case myjson::JParserState::PARSE_OBJECT_GET_KEY:
			if (cur_ch == '"')
			{
				cur_parse_state = myjson::JParserState::PARSE_STRING_START;
				start_symbol_stack.push(cur_ch);
				cur_index_of_jtext++;
				cur_key = this->parseString();
				cur_index_of_jtext++;
				cur_parse_state = myjson::JParserState::PARSE_CONTINUE;
			}
			else
			{
				reportError(myjson::JParserErrorType::KEY_ERROR);
				cur_index_of_jtext++;
			}
			break;
		case myjson::JParserState::PARSE_CONTINUE:
			if (cur_ch == ':')
			{
				cur_parse_state = myjson::JParserState::PARSE_OBJECT_GET_VALUE;
				cur_index_of_jtext++;
			}
			else if (cur_ch == ',')
			{
				cur_parse_state = myjson::JParserState::PARSE_OBJECT_GET_KEY_VALUE_FINISH;
			}
			else
			{
				reportError(myjson::JParserErrorType::KEY_VALUE_CONNECTION);
				cur_index_of_jtext++;
			}
			break;
		case myjson::JParserState::PARSE_OBJECT_GET_VALUE:
			if (cur_key == nullptr)
			{
				reportError(myjson::JParserErrorType::NO_KEY);
				cur_parse_state = myjson::JParserState::PARSE_OBJECT_GET_KEY;
				break;
			}
			cur_jnode = this->parseValue();
			if (cur_jnode == nullptr)
			{
				reportError(myjson::JParserErrorType::NO_VALUE);
			}
			else
			{
				ans_jnode->updateObjectKeyValue((*cur_key), std::move((*cur_jnode)));
				delete cur_key;
				delete cur_jnode;
			}
			cur_index_of_jtext++;
			cur_parse_state = myjson::JParserState::PARSE_OBJECT_GET_KEY_VALUE_FINISH;
			break;
		case myjson::JParserState::PARSE_OBJECT_GET_KEY_VALUE_FINISH:
			if (cur_ch == ',')
			{
				cur_index_of_jtext++;
				cur_parse_state = myjson::JParserState::PARSE_OBJECT_GET_KEY;
			}
			else if (cur_ch == '}')
			{
				cur_parse_state = myjson::JParserState::PARSE_OBJECT_FINISH;
			}
			else
			{
				reportError(myjson::JParserErrorType::KEY_VALUE_FINISH_ERROR);
				cur_index_of_jtext++;
			}
			break;
		case myjson::JParserState::PARSE_OBJECT_FINISH:
			start_symbol_stack.pop();
			if (!start_symbol_stack.empty())
			{
				cur_parse_state = myjson::JParserState::PARSE_CONTINUE;
				cur_index_of_jtext++;
			}
			else cur_parse_state = myjson::JParserState::PARSE_FINISH;
			if_finish = true;
			break;
		default:
			return nullptr;
		}
	}
	return ans_jnode;
}

std::string* myjson::JSONParser::parseString()
{
	if (cur_index_of_jtext >= jtext_size)
	{
		reportError(myjson::JParserErrorType::INDEX_OUT_OF_BOUND);
		return nullptr;
	}
	std::string* ans_string = new std::string();
	bool if_finish = false;
	while (cur_index_of_jtext < jtext_size && !if_finish)
	{
		cur_ch = jtext[cur_index_of_jtext];
		switch (cur_parse_state)
		{
		case myjson::JParserState::PARSE_STRING_START:
			if (cur_ch != '"')
			{
				cur_parse_state = myjson::JParserState::PARSE_STRING_GET_NEXT_CHAR;
			}
			else
			{
				cur_parse_state = myjson::JParserState::PARSE_STRING_FINISH;
			}
			break;
		case myjson::JParserState::PARSE_STRING_GET_NEXT_CHAR:
			if (cur_ch != '"')
			{
				ans_string->push_back(cur_ch);
				cur_index_of_jtext++;
			}
			else
			{
				cur_parse_state = myjson::JParserState::PARSE_STRING_FINISH;
			}
			break;
		case myjson::JParserState::PARSE_STRING_FINISH:
			start_symbol_stack.pop();
			if (!start_symbol_stack.empty())cur_parse_state = myjson::JParserState::PARSE_CONTINUE;
			else cur_parse_state = myjson::JParserState::PARSE_FINISH;
			if_finish = true;
			break;
		}
	}
	if (cur_parse_state != myjson::JParserState::PARSE_CONTINUE && cur_parse_state != myjson::JParserState::PARSE_FINISH)
	{
		reportError(myjson::JParserErrorType::INCOMPLETE_STRING);
		cur_parse_state = myjson::JParserState::PARSE_FINISH;
	}
	return ans_string;
}

myjson::JNode* myjson::JSONParser::parseArray()
{
	if (cur_index_of_jtext >= jtext_size)
	{
		reportError(myjson::JParserErrorType::INDEX_OUT_OF_BOUND);
		return nullptr;
	}
	myjson::JNode* ans_jnode = new myjson::JNode(myjson::JNodeType::JSON_ARRAY);
	bool if_finish = false;
	myjson::JNode* cur_value = nullptr;
	while (cur_index_of_jtext < jtext_size && !if_finish)
	{
		cur_ch = jtext[cur_index_of_jtext];
		if (cur_ch == ']')break;
		switch (cur_parse_state)
		{
		case myjson::JParserState::PARSE_ARRAY_GET_VALUE:
			cur_value = this->parseValue();
			cur_index_of_jtext++;
			if (cur_value != nullptr)
			{
				ans_jnode->addArrayValue(std::move(*cur_value));
				delete cur_value;
				cur_value = nullptr;
			}
			cur_parse_state = myjson::JParserState::PARSE_CONTINUE;
			break;
		case myjson::JParserState::PARSE_CONTINUE:
			if (cur_ch == ']')
			{
				if_finish = true;
				start_symbol_stack.pop();
				if (start_symbol_stack.empty())cur_parse_state = myjson::JParserState::PARSE_FINISH;
			}
			else if (cur_ch == ',')
			{
				cur_parse_state = myjson::JParserState::PARSE_ARRAY_GET_VALUE;
				cur_index_of_jtext++;
			}
			else
			{
				reportError(myjson::JParserErrorType::ARRAY_VALUES_CONNECTION);
				cur_index_of_jtext++;
			}
			break;
		case myjson::JParserState::PARSE_FINISH:
			if_finish = true;
			break;
		}
	}
	return ans_jnode;
}

myjson::JNode* myjson::JSONParser::parseNullOrTrueOrFalse()
{
	myjson::JNode* ans_jnode = nullptr;
	bool if_finish = false;
	while (cur_index_of_jtext < jtext_size && !if_finish)
	{
		cur_ch = jtext[cur_index_of_jtext];
		switch (cur_parse_state)
		{
		case myjson::JParserState::PARSE_SPECIAL_VALUE_START:
			if (cur_ch == 'n')
			{
				cur_parse_state = myjson::JParserState::PARSE_NULL;
			}
			else if (cur_ch == 't')
			{
				cur_parse_state = myjson::JParserState::PARSE_TRUE;
			}
			else if (cur_ch == 'f')
			{
				cur_parse_state = myjson::JParserState::PARSE_FALSE;
			}
			cur_index_of_jtext++;
			break;
		case myjson::JParserState::PARSE_NULL:
			if (cur_ch != 'u')
			{
				reportError(myjson::JParserErrorType::VALUE_ERROR);
				if_finish = true;
				break;
			}
			cur_index_of_jtext++;
			if (cur_index_of_jtext >= jtext_size)
			{
				reportError(myjson::JParserErrorType::INDEX_OUT_OF_BOUND);
				cur_parse_state = myjson::JParserState::PARSE_FINISH;
				if_finish = true;
				break;
			}
			cur_ch = jtext[cur_index_of_jtext];
			if (cur_ch != 'l')
			{
				reportError(myjson::JParserErrorType::VALUE_ERROR);
				if_finish = true;
				break;
			}
			cur_index_of_jtext++;
			if (cur_index_of_jtext >= jtext_size)
			{
				reportError(myjson::JParserErrorType::INDEX_OUT_OF_BOUND);
				cur_parse_state = myjson::JParserState::PARSE_FINISH;
				if_finish = true;
				break;
			}
			cur_ch = jtext[cur_index_of_jtext];
			if (cur_ch != 'l')
			{
				reportError(myjson::JParserErrorType::VALUE_ERROR);
				if_finish = true;
				break;
			}
			cur_parse_state = myjson::JParserState::PARSE_SPECIAL_VALUE_FINISH;
			ans_jnode = new myjson::JNode();
			break;
		case myjson::JParserState::PARSE_TRUE:
			if (cur_ch != 'r')
			{
				reportError(myjson::JParserErrorType::VALUE_ERROR);
				if_finish = true;
				break;
			}
			cur_index_of_jtext++;
			if (cur_index_of_jtext >= jtext_size)
			{
				reportError(myjson::JParserErrorType::INDEX_OUT_OF_BOUND);
				cur_parse_state = myjson::JParserState::PARSE_FINISH;
				if_finish = true;
				break;
			}
			cur_ch = jtext[cur_index_of_jtext];
			if (cur_ch != 'u')
			{
				reportError(myjson::JParserErrorType::VALUE_ERROR);
				if_finish = true;
				break;
			}
			cur_index_of_jtext++;
			if (cur_index_of_jtext >= jtext_size)
			{
				reportError(myjson::JParserErrorType::INDEX_OUT_OF_BOUND);
				cur_parse_state = myjson::JParserState::PARSE_FINISH;
				if_finish = true;
				break;
			}
			cur_ch = jtext[cur_index_of_jtext];
			if (cur_ch != 'e')
			{
				reportError(myjson::JParserErrorType::VALUE_ERROR);
				if_finish = true;
				break;
			}
			cur_parse_state = myjson::JParserState::PARSE_SPECIAL_VALUE_FINISH;
			ans_jnode = new myjson::JNode(true);
			break;
		case myjson::JParserState::PARSE_FALSE:
			if (cur_ch != 'a')
			{
				reportError(myjson::JParserErrorType::VALUE_ERROR);
				if_finish = true;
				break;
			}
			cur_index_of_jtext++;
			if (cur_index_of_jtext >= jtext_size)
			{
				reportError(myjson::JParserErrorType::INDEX_OUT_OF_BOUND);
				cur_parse_state = myjson::JParserState::PARSE_FINISH;
				if_finish = true;
				break;
			}
			cur_ch = jtext[cur_index_of_jtext];
			if (cur_ch != 'l')
			{
				reportError(myjson::JParserErrorType::VALUE_ERROR);
				if_finish = true;
				break;
			}
			cur_index_of_jtext++;
			if (cur_index_of_jtext >= jtext_size)
			{
				reportError(myjson::JParserErrorType::INDEX_OUT_OF_BOUND);
				cur_parse_state = myjson::JParserState::PARSE_FINISH;
				if_finish = true;
				break;
			}
			cur_ch = jtext[cur_index_of_jtext];
			if (cur_ch != 's')
			{
				reportError(myjson::JParserErrorType::VALUE_ERROR);
				if_finish = true;
				break;
			}
			cur_index_of_jtext++;
			if (cur_index_of_jtext >= jtext_size)
			{
				reportError(myjson::JParserErrorType::INDEX_OUT_OF_BOUND);
				cur_parse_state = myjson::JParserState::PARSE_FINISH;
				if_finish = true;
				break;
			}
			cur_ch = jtext[cur_index_of_jtext];
			if (cur_ch != 'e')
			{
				reportError(myjson::JParserErrorType::VALUE_ERROR);
				if_finish = true;
				break;
			}
			cur_parse_state = myjson::JParserState::PARSE_SPECIAL_VALUE_FINISH;
			ans_jnode = new myjson::JNode(false);
			break;
		case myjson::JParserState::PARSE_SPECIAL_VALUE_FINISH:
			if (start_symbol_stack.empty())cur_parse_state = myjson::JParserState::PARSE_FINISH;
			else cur_parse_state = myjson::JParserState::PARSE_CONTINUE;
			if_finish = true;
			break;
		}
	}
	return ans_jnode;
}

myjson::JNode* myjson::JSONParser::parseNumber()
{
	myjson::JNode* cur_jnode = nullptr;
	bool if_int = true;
	bool if_finish = false;
	std::string assist = "";
	while (cur_index_of_jtext < jtext_size && !if_finish)
	{
		cur_ch = jtext[cur_index_of_jtext];
		switch (cur_parse_state)
		{
		case myjson::JParserState::PARSE_NUMBER_START:
			assist += cur_ch;
			cur_index_of_jtext++;
			cur_parse_state = myjson::JParserState::PARSE_INT;
			break;
		case myjson::JParserState::PARSE_INT:
			if (cur_ch == '.')
			{
				cur_parse_state = myjson::JParserState::PARSE_DOUBLE;
				assist += cur_ch;
				if_int = false;
			}
			else if ((cur_ch - '0') >= 0 && (cur_ch - '0') <= 9)
			{
				assist += cur_ch;
			}
			else
			{
				cur_parse_state = myjson::JParserState::PARSE_NUMBER_FINISH;
				cur_index_of_jtext--;
				break;
			}
			cur_index_of_jtext++;
			break;
		case myjson::JParserState::PARSE_DOUBLE:
			if ((cur_ch - '0') >= 0 && (cur_ch - '0') <= 9)
			{
				assist += cur_ch;
				cur_index_of_jtext++;
			}
			else
			{
				cur_parse_state = myjson::JParserState::PARSE_NUMBER_FINISH;
				cur_index_of_jtext--;
				break;
			}
			break;
		case myjson::JParserState::PARSE_NUMBER_FINISH:
			if (start_symbol_stack.empty())cur_parse_state = myjson::JParserState::PARSE_FINISH;
			else cur_parse_state = myjson::JParserState::PARSE_CONTINUE;
			if_finish = true;
			break;
		}
	}
	if (if_int)
	{
		try
		{
			std::int64_t cur_int = std::stoll(assist);
			cur_jnode = new myjson::JNode(cur_int);
		}
		catch (const std::exception& e)
		{
			reportError(myjson::JParserErrorType::INT_ERROR);
		}
	}
	else
	{
		try
		{
			double cur_double = std::stod(assist);
			cur_jnode = new myjson::JNode(cur_double);
		}
		catch (const std::exception& e)
		{
			reportError(myjson::JParserErrorType::DOUBLE_ERROR);
		}
	}
	return cur_jnode;
}

myjson::JNode* myjson::JSONParser::parseValue()
{
	myjson::JNode* cur_jnode = nullptr;
	if (cur_ch == '{')
	{
		cur_parse_state = myjson::JParserState::PARSE_OBJECT_GET_KEY;
		start_symbol_stack.push('{');
		cur_index_of_jtext++;
		cur_jnode = this->parseObject();
	}
	else if (cur_ch == '"')
	{
		cur_parse_state = myjson::JParserState::PARSE_STRING_START;
		start_symbol_stack.push('\"');
		cur_index_of_jtext++;
		std::string* assist = this->parseString();
		if (assist != nullptr)
		{
			cur_jnode = new myjson::JNode();
			(*cur_jnode) = std::move(myjson::JNode((*assist)));
			delete assist;
		}
	}
	else if (cur_ch == '[')
	{
		cur_parse_state = myjson::JParserState::PARSE_ARRAY_GET_VALUE;
		start_symbol_stack.push('[');
		cur_index_of_jtext++;
		cur_jnode = this->parseArray();
	}
	else if (cur_ch == 'n' || cur_ch == 't' || cur_ch == 'f')
	{
		cur_parse_state = myjson::JParserState::PARSE_SPECIAL_VALUE_START;
		cur_jnode = this->parseNullOrTrueOrFalse();
	}
	else if (((cur_ch - '0') >= 0 && (cur_ch - '0') <= 9) || cur_ch == '-')
	{
		cur_parse_state = myjson::JParserState::PARSE_NUMBER_START;
		cur_jnode = this->parseNumber();
	}
	else
	{
		reportError(myjson::JParserErrorType::VALUE_ERROR);
	}
	return cur_jnode;
}
